<template>
  <div :class="[{ 'drag-layout-bg': isHideGrid }, 'drag-layout']" id="layout">
    <div
      v-for="(item, index) in dataSource"
      :key="`layout-${index}`"
      :class="[
        item.type === 'child' ? 'drag-container-child' : 'drag-container',
        { 'drag-container-function': item.model === 'function' },
        'drag-parent'
      ]"
      :id="`container-${index}`"
      @mousedown.stop="handleMousedown($event, index)"
      :style="{
        top: item.top + 'px',
        left: item.left + 'px',
        width: getParentWidth(item).width + 'px',
        height: getParentWidth(item).height + 'px'
      }"
    >
      <!-- 旋转 -->
      <div
        v-if="item.type === 'child'"
        class="drag-container-item-rotate drag-layout-assist drag-parent"
        @click="rotateChild(index)"
      ></div>
      <!-- 缩放 -->
      <div
        class="drag-layout-assist-border drag-parent"
        v-if="item.type !== 'child'"
        :style="{
          width: getParentWidth(item).width + 20 + 'px',
          height: getParentWidth(item).height + 20 + 'px',
          display:
            !isMouseDown && !isResizing
              ? 'none'
              : index !== selectedIndex || 'inline-block'
        }"
      >
        <div
          v-if="item.type === 'parent' && !item.child"
          class="drag-container-item-rotate drag-layout-assist drag-parent"
          @click="rotateChild(index)"
        ></div>
        <div
          class="del drag-layout-assist drag-parent"
          @click="delLayout(index)"
        >
          删
        </div>
        <div
          class="hide drag-layout-assist drag-parent"
          v-if="item.type === 'parent' && item.child"
          @click="hideParentLayout(index)"
        >
          隐
        </div>
        <div
          v-for="item in dragPointList"
          :key="item"
          :class="[
            'drag-point',
            'drag-layout-assist',
            'drag-parent',
            `point-${item}`
          ]"
          @mousedown.stop.prevent="handlePointDown($event, item, index)"
        ></div>
      </div>
      <!-- 名称 -->
      <span
        v-if="item.name"
        :class="[
          item.type !== 'child'
            ? 'drag-container-name'
            : 'drag-container-item-name',
          { 'drag-container-name-function': item.model === 'function' },
          'drag-parent'
        ]"
        >{{ item.name }}</span
      >
      <input
        type="text"
        style="z-index: 99"
        v-if="item.isEdit && !item.name"
        @keyup.enter="inputName($event, index)"
      />

      <div
        class="drag-container-item drag-child"
        v-for="(itemChild, indexChild) in item.child"
        :key="itemChild.name"
        :id="`item-${index}-${indexChild}`"
        @mousedown.stop.prevent="
          handleChildMousedown($event, index, indexChild)
        "
        :style="{
          width: getChildWidth(item.sizeType, itemChild.direction).width + 'px',
          height:
            getChildWidth(item.sizeType, itemChild.direction).height + 'px',
          top: itemChild.top + 'px',
          left: itemChild.left + 'px'
        }"
      >
        {{ itemChild.name }}
        <div
          class="drag-container-item-rotate drag-child"
          @click="rotateChild(index, indexChild)"
        ></div>
      </div>
    </div>
  </div>
</template>

<script>
import {
  roundOffset,
  getLimitVal,
  getParentWidth,
  getChildWidth
} from "@/utils/tool";
export default {
  name: "DragLayout",
  model: {
    prop: "dataSource",
    event: "change"
  },
  props: {
    dataSource: {
      type: Array,
      default: () => []
    },
    isHideGrid: {
      type: Boolean,
      default: true
    },
    size: {
      type: String,
      default: "medium"
    }
  },
  data() {
    return {
      dragging: false,
      childDragging: false,
      pointDragging: false,
      isResizing: false, // 不在缩放状态并且鼠标没有按下，缩放框消失
      isMouseDown: false,
      pointType: "",
      pointRecordX: 0,
      pointRecordY: 0,
      pointRecordWidth: 0,
      pointRecordHeight: 0,
      pointRecordTop: 0,
      pointRecordLeft: 0,
      recordTop: 0,
      recordLeft: 0,
      selectedIndex: null,
      selectedChildIndex: null,
      offsetX: 0, //偏移值
      offsetY: 0, //偏移值
      limitRight: 0,
      limitBottom: 0,
      limitWidth: 64,
      limitHeight: 90,
      limitChildRight: 0,
      limitChildBottom: 0,
      selectedEle: null,
      selectedChildEle: null,
      dragPointList: ["lt", "mt", "rt", "mr", "rb", "mb", "lb", "ml"],
      pointX: 0,
      pointY: 0
    };
  },
  watch: {
    size(val) {
      console.log("val", val);
      this.dataSource.forEach(ele => {
        ele.sizeType = val;
      });
    }
  },
  mounted() {
    // 事件名，事件函数，布尔值（指定事件是否在捕获或冒泡阶段执行）
    document.documentElement.addEventListener(
      "mousemove",
      this.handleMouseMove,
      true
    );
    document.documentElement.addEventListener(
      "mouseup",
      this.handleMouseUp,
      true
    );
  },
  methods: {
    getParentLayoutLimit(val = []) {
      let itemArr = [];
      for (let i = 0; i < val.length; i++) {
        if (
          val[i].className &&
          val[i].className.includes("drag-container-item")
        ) {
          const { offsetWidth, offsetLeft, offsetHeight, offsetTop } = val[i];
          itemArr.push({
            width: offsetWidth + offsetLeft,
            height: offsetHeight + offsetTop
          });
        }
      }
      const width = itemArr.map(ele => ele.width).sort((a, b) => b - a)[0];
      const height = itemArr.map(ele => ele.height).sort((a, b) => b - a)[0];
      this.limitWidth = width ? width + 4 : 64;
      this.limitHeight = height ? height + 5 : 90;
    },
    // 选中元素
    handleMousedown(e, index) {
      const { className } = e.target;
      const {
        offsetLeft,
        offsetTop,
        offsetWidth,
        offsetHeight
      } = document.getElementById("layout");
      const container = document.getElementById(`container-${index}`);
      this.selectedEle = container;

      this.getParentLayoutLimit(container.childNodes);

      if (className.includes("drag-parent")) {
        // +10 是因为标题的偏移
        let offsetX = 0;
        let offsetY = 0;
        switch (className) {
          case "drag-container-name":
            offsetX = e.offsetX + 10;
            offsetY = e.offsetY + 10;
            break;
          case "drag-layout-assist-border":
            offsetX = e.offsetX - 10;
            offsetY = e.offsetY - 10;
            break;
          default:
            offsetX = e.offsetX;
            offsetY = e.offsetY;
            break;
        }
        if (this.dataSource[index].direction === "horizontal") {
          this.limitRight = offsetWidth - this.dataSource[index].height;
          this.limitBottom = offsetHeight - this.dataSource[index].width;
        } else {
          this.limitRight = offsetWidth - this.dataSource[index].width;
          this.limitBottom = offsetHeight - this.dataSource[index].height;
        }

        this.offsetX = offsetLeft + offsetX;
        this.offsetY = offsetTop + offsetY;
        this.dragging = true;
        this.selectedIndex = index;
        this.selectedEle.style.zIndex = 10;
        this.isResizing = true;
        this.isMouseDown = true;
      }
    },
    // 父元素拖拽的点
    handlePointDown(e, type, index) {
      const { clientX, clientY } = e;
      const container = document.getElementById(`container-${index}`);
      this.getParentLayoutLimit(container.childNodes);

      const { offsetWidth, offsetHeight, offsetLeft, offsetTop } = container;
      this.recordTop = container.offsetTop;
      this.recordLeft = container.offsetLeft;

      this.pointType = type;
      // 保留之前 进行缩放
      this.pointRecordWidth = offsetWidth;
      this.pointRecordHeight = offsetHeight;
      this.pointRecordLeft = offsetLeft;
      this.pointRecordTop = offsetTop;

      this.pointDragging = true;
      this.selectedIndex = index;
      this.pointRecordX = clientX;
      this.pointRecordY = clientY;
      this.isResizing = true;
      this.isMouseDown = true;
    },
    // 子元素选中
    handleChildMousedown(e, index, indexChild) {
      const { className } = e.target;
      this.selectedChildEle = e.target;
      const { offsetLeft, offsetTop } = document.getElementById("layout");
      const container = document.getElementById(`container-${index}`);
      const width = e.target.offsetWidth;
      const height = e.target.offsetHeight;
      if (className.includes("drag-child")) {
        this.childDragging = true;
        this.selectedIndex = index;
        this.selectedChildIndex = indexChild;
        this.layoutOffsetLeft =
          container.offsetLeft + offsetLeft + e.offsetX + 5;
        this.layoutOffsetTop = container.offsetTop + offsetTop + e.offsetY + 30;

        // 禁止拖出父元素
        this.limitChildRight = container.offsetWidth - width - 5;
        this.limitChildBottom = container.offsetHeight - height - 5;

        this.selectedEleOffsetX = e.clientX;
        this.selectedEleOffsetY = e.clientY;
        this.isResizing = true;
        this.isMouseDown = true;
      }
    },
    // 鼠标移动
    handleMouseMove(e) {
      const mouseX = e.pageX;
      const mouseY = e.pageY;
      const { className } = e.target;
      // 整体拖动
      if (this.dragging) {
        const formatX = mouseX - this.offsetX;
        const formatY = mouseY - this.offsetY;

        this.dataSource[this.selectedIndex].left = getLimitVal(
          formatX,
          this.limitRight
        );
        this.dataSource[this.selectedIndex].top = getLimitVal(
          formatY,
          this.limitBottom
        );
      } else if (this.childDragging) {
        const { child } = this.dataSource[this.selectedIndex];
        const formatX = mouseX - this.layoutOffsetLeft;
        const formatY = mouseY - this.layoutOffsetTop;
        // 子项目拖动
        child[this.selectedChildIndex].left = getLimitVal(
          formatX,
          this.limitChildRight - 5
        );
        child[this.selectedChildIndex].top =
          getLimitVal(formatY, this.limitChildBottom - 30) + 25;
      } else if (this.pointDragging) {
        if (
          className.includes("drag-parent") ||
          className.includes("drag-child") ||
          className.includes("drag-layout")
        ) {
          this.resize(e);
        }
      }
      // 离开拖动区域释放
      if (
        !className.includes("drag-parent") &&
        !className.includes("drag-child")
      ) {
        this.isResizing = false;
        if (!this.isMouseDown && !this.resetData) {
          this.resetData();
        }
      }
    },
    inputName(e, index) {
      console.log(e.target.value);
      this.dataSource[index].name = e.target.value;
    },
    // 缩放
    resize(e) {
      const { clientX, clientY } = e;
      const diffX = roundOffset(clientX - this.pointRecordX);
      const diffY = roundOffset(clientY - this.pointRecordY);
      let width = this.dataSource[this.selectedIndex].width;
      let height = this.dataSource[this.selectedIndex].height;
      let top = this.dataSource[this.selectedIndex].top;
      let left = this.dataSource[this.selectedIndex].left;

      if (this.pointType === "lt") {
        //往左上缩放
        width = this.pointRecordWidth - diffX;
        height = this.pointRecordHeight - diffY;
        left = this.pointRecordLeft + diffX;
        top = this.pointRecordTop + diffY;
      } else if (this.pointType === "mt") {
        //往上缩放
        height = this.pointRecordHeight - diffY;
        top = this.pointRecordTop + diffY;
      } else if (this.pointType === "rt") {
        //往右上缩放
        width = this.pointRecordWidth + diffX;
        height = this.pointRecordHeight - diffY;
        top = this.pointRecordTop + diffY;
      } else if (this.pointType === "mr") {
        //往右缩放
        width = this.pointRecordWidth + diffX;
      } else if (this.pointType === "rb") {
        //往右下缩放
        width = this.pointRecordWidth + diffX;
        height = this.pointRecordHeight + diffY;
      } else if (this.pointType === "mb") {
        //往下缩放
        height = this.pointRecordHeight + diffY;
      } else if (this.pointType === "lb") {
        //往左下缩放
        width = this.pointRecordWidth - diffX;
        height = this.pointRecordHeight + diffY;
        left = this.pointRecordLeft + diffX;
      } else if (this.pointType === "ml") {
        //往左缩放
        width = this.pointRecordWidth - diffX;
        left = this.pointRecordLeft + diffX;
      }

      if (width < this.limitWidth && height < this.limitHeight) {
        this.dataSource[this.selectedIndex].width = this.limitWidth;
        this.dataSource[this.selectedIndex].height = this.limitHeight;
        this.dataSource[this.selectedIndex].top = this.recordTop;
        this.dataSource[this.selectedIndex].left = this.recordLeft;
      } else if (width < this.limitWidth) {
        this.dataSource[this.selectedIndex].width = this.limitWidth;
        this.dataSource[this.selectedIndex].height = height;
        this.dataSource[this.selectedIndex].top = top;
        this.dataSource[this.selectedIndex].left = this.recordLeft;
      } else if (height < this.limitHeight) {
        this.dataSource[this.selectedIndex].top = this.recordTop;
        this.dataSource[this.selectedIndex].left = left;
        this.dataSource[this.selectedIndex].width = width;
        this.dataSource[this.selectedIndex].height = this.limitHeight;
      } else {
        this.dataSource[this.selectedIndex].width = width;
        this.dataSource[this.selectedIndex].height = height;

        this.dataSource[this.selectedIndex].top = top;
        this.dataSource[this.selectedIndex].left = left;
      }
    },
    /**
     * 旋转子元素，通过宽高变化进行竖放和横放
     *
     * 注意： 旋转后，子元素可能超出父元素，需要做处理
     * 根据子元素的偏移值和宽高 跟父元素对比
     */
    rotateChild(index, indexChild) {
      if (indexChild === undefined) {
        // 独立病床旋转
        let { direction } = this.dataSource[index];
        const {
          offsetLeft,
          offsetTop,
          offsetWidth,
          offsetHeight
        } = document.getElementById(`container-${index}`);
        const layout = document.getElementById("layout");

        const differenceWidth =
          layout.offsetWidth - (offsetLeft + offsetHeight);
        const differenceHeight =
          layout.offsetHeight - (offsetTop + offsetWidth);

        if (direction === "horizontal") {
          this.dataSource[index].direction = "vertical";
        } else {
          this.dataSource[index].direction = "horizontal";
        }
        // 旋转后判断是否超出  然后进行偏移  要取整值
        if (differenceWidth < 0) {
          this.dataSource[index].left = roundOffset(
            this.dataSource[index].left + differenceWidth
          );
        }
        if (differenceHeight < 0) {
          this.dataSource[index].top = roundOffset(
            this.dataSource[index].top + differenceHeight
          );
        }
      } else {
        // 房间病床旋转
        let { direction } = this.dataSource[index].child[indexChild];
        const {
          offsetLeft,
          offsetTop,
          offsetWidth,
          offsetHeight
        } = document.getElementById(`item-${index}-${indexChild}`);

        const container = document.getElementById(`container-${index}`);
        const differenceWidth =
          container.offsetWidth - (offsetLeft + offsetHeight);
        const differenceHeight =
          container.offsetHeight - (offsetTop + offsetWidth);

        // 旋转
        if (direction === "horizontal") {
          this.dataSource[index].child[indexChild].direction = "vertical";
        } else {
          this.dataSource[index].child[indexChild].direction = "horizontal";
        }
        // 旋转后判断是否超出  然后进行偏移 要取整值
        if (differenceWidth < 0) {
          this.dataSource[index].child[indexChild].left =
            roundOffset(
              this.dataSource[index].child[indexChild].left + differenceWidth
            ) - 5;
        }
        if (differenceHeight < 0) {
          this.dataSource[index].child[indexChild].top =
            roundOffset(
              this.dataSource[index].child[indexChild].top + differenceHeight
            ) - 5;
        }
      }
    },
    /**
     * 删除元素
     */
    delLayout(index) {
      this.dataSource.splice(index, 1);
      this.resetData();
    },
    /**
     * 隐藏父元素
     * 把子元素拿出来 位置不动
     *
     */
    hideParentLayout(index) {
      const parentEle = document.getElementById(`container-${index}`);
      const arr = this.dataSource[index].child.map((ele, indexChild) => {
        const { offsetLeft, offsetTop } = document.getElementById(
          `item-${index}-${indexChild}`
        );
        // +2 是消除父元素的边框影响
        return {
          ...ele,
          left: offsetLeft + parentEle.offsetLeft + 2,
          top: offsetTop + +parentEle.offsetTop + 2,
          type: "child",
          width: 90,
          height: 120,
          sizeType: "medium"
        };
      });
      this.dataSource.splice(index, 1);
      this.dataSource.push(...arr);
      console.log(arr);
    },
    // 鼠标松开
    handleMouseUp() {
      this.isMouseDown = false;
      //   if (!this.isMouseDown && !this.resetData) {
      //   this.resetData();
      //   }
      this.dragging = false;
      this.childDragging = false;
      this.pointDragging = false;
      this.pointType = "";
      this.pointRecordX = 0;
      this.pointRecordY = 0;
    },
    resetData() {
      this.dragging = false;
      this.childDragging = false;

      this.selectedIndex = null;
      this.selectedEleOffsetX = 0;
      this.selectedEleOffsetY = 0;
      this.pointType = "";
      this.pointDragging = false;
      this.selectedIndex = null;
      this.pointRecordX = 0;
      this.pointRecordY = 0;
      if (this.selectedEle) {
        this.selectedEle.style.zIndex = 0;
        this.selectedEle = null;
      }
      if (this.selectedChildEle) {
        this.selectedChildEle.style.zIndex = 0;
        this.selectedChildEle = null;
      }
    },
    getParentWidth,
    getChildWidth
  }
};
</script>

<style lang="scss" scoped>
@import "./layout.scss";
</style>
